/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

using System;

using TwoWayHashMap = argot.util.TwoWayHashMap;

using DateS64 = argot.common.DateS64;
using U16ArrayByte = argot.common.U16ArrayByte;
using U32ArrayByte = argot.common.U32ArrayByte;
using BigEndianSignedByte = argot.common.BigEndianSignedByte;
using BigEndianSignedInteger = argot.common.BigEndianSignedInteger;
using BigEndianSignedLong = argot.common.BigEndianSignedLong;
using BigEndianSignedShort = argot.common.BigEndianSignedShort;
using BigEndianUnsignedByte = argot.common.BigEndianUnsignedByte;
using BigEndianUnsignedInteger = argot.common.BigEndianUnsignedInteger;
using BigEndianUnsignedLong = argot.common.BigEndianUnsignedLong;
using BigEndianUnsignedShort = argot.common.BigEndianUnsignedShort;
using U8Boolean = argot.common.U8Boolean;
using U8Ascii = argot.common.U8Ascii;
using U32UTF8 = argot.common.U32UTF8;
using MetaDefinition = argot.meta.MetaDefinition;

namespace argot
{
	
	/// <summary> 
	/// A TypeMap is used to map an external systems type identifiers to
	/// the internal TypeLibrary of the host system.
	/// </summary>
	public class TypeMap
	{

		public static int NOTYPE = - 1;
		
		internal TypeLibrary _library;
		internal TwoWayHashMap _map;
		
		
		public TypeMap(TypeLibrary library)
		{
			_library = library;
			_map = new TwoWayHashMap();
		}
		
		virtual public TypeLibrary Library
		{
			get
			{
				return _library;
			}
			
		}

		virtual public System.Collections.IEnumerator Iterator
		{
			get
			{
				return _map.GetEnumerator();
			}
			
		}

		public virtual int Size()
		{
			return _map.Size();
		}

		public virtual int MapCommon(int base_Renamed)
		{
			// Map Unsigned types.
			if (!IsValid(BigEndianUnsignedByte.TYPENAME))
				Map(base_Renamed + 1, Library.GetId(BigEndianUnsignedByte.TYPENAME));
			
			if (!IsValid(BigEndianUnsignedShort.TYPENAME))
				Map(base_Renamed + 2, Library.GetId(BigEndianUnsignedShort.TYPENAME));
			
			if (!IsValid(BigEndianUnsignedInteger.TYPENAME))
				Map(base_Renamed + 3, Library.GetId(BigEndianUnsignedInteger.TYPENAME));
			
			if (!IsValid(BigEndianUnsignedLong.TYPENAME))
				Map(base_Renamed + 4, Library.GetId(BigEndianUnsignedLong.TYPENAME));
			
			// Map Signed types.
			if (!IsValid(BigEndianSignedByte.TYPENAME))
				Map(base_Renamed + 5, Library.GetId(BigEndianSignedByte.TYPENAME));
			
			if (!IsValid(BigEndianSignedShort.TYPENAME))
				Map(base_Renamed + 6, Library.GetId(BigEndianSignedShort.TYPENAME));
			
			if (!IsValid(BigEndianSignedInteger.TYPENAME))
				Map(base_Renamed + 7, Library.GetId(BigEndianSignedInteger.TYPENAME));
			
			if (!IsValid(BigEndianSignedLong.TYPENAME))
				Map(base_Renamed + 8, Library.GetId(BigEndianSignedLong.TYPENAME));
			
			if (!IsValid(U8Ascii.TYPENAME))
				Map(base_Renamed + 9, Library.GetId(U8Ascii.TYPENAME));
			
			if (!IsValid(U32UTF8.TYPENAME))
				Map(base_Renamed + 10, Library.GetId(U32UTF8.TYPENAME));
			
			if (!IsValid(U16ArrayByte.TYPENAME))
				Map(base_Renamed + 11, Library.GetId(U16ArrayByte.TYPENAME));
			
			if (!IsValid(U32ArrayByte.TYPENAME))
				Map(base_Renamed + 12, Library.GetId(U32ArrayByte.TYPENAME));
			
			if (!IsValid(U8Boolean.TYPENAME))
				Map(base_Renamed + 13, Library.GetId(U8Boolean.TYPENAME));
			
			if (!IsValid(DateS64.TYPENAME))
				Map(base_Renamed + 14, Library.GetId(DateS64.TYPENAME));
			
			return base_Renamed + 14;
		}
		
		public virtual void Map(int id, int systemId)
		{
			System.Int32 i;

			if (systemId == NOTYPE)
				throw new TypeException("typeid not valid" + systemId);
			
			// check if it hasn't already been mapped.
			if ( (i = _map.FindValue( id )) != -1 )
			{
				if ( i != systemId )
					throw new TypeException("id alreadymappe to different type");

				// already mapped to the same value.
				return;
			}

			_map.Add( id, systemId );
		}
		
		/// <summary> 
		/// This checks if the id, name & structure are the same as used
		/// in the map.  The byte[] must follow the TypeMapCore type id's.
		/// Any function or references must be valid in the context of this
		/// TypeMap.
		/// 
		/// This is like the register version below, however in some cases
		/// like a protocol you just need to check if the id's are the same.
		/// </summary>

		public virtual bool IsSame(int id, System.String name, byte[] structure, ReferenceTypeMap coreMap)
		{
			// First check if we can find the same identifier.
			int i;
			
			try
			{
				i = _library.GetId(name);
			}
			catch (TypeException)
			{
				return false;
			}
			
			// Are the identifiers the same.
			if (id != i)
			{
				return false;
			}

			// Are the definitions the same.
			try
			{
				// read the definition.
				coreMap.ReferenceMap = this;
				TypeElement definition = ReadStructure(coreMap, structure);
				
				// check what we've read with the local version.
				TypeElement localStruct = _library.GetStructure(i);
				return TypeHelper.StructureMatches(coreMap, definition, localStruct);
			}
			catch (TypeException)
			{
				return false;
			}
		}
		
		/// <summary> 
		/// This is used to map a client or external system set of ids
		/// to the local set of definitions.  

		/// </summary>


		public virtual TypeElement ReadStructure(ReferenceTypeMap core, byte[] structure)
		{
			// Get hold of the type map required to read the structure.
			System.IO.MemoryStream bais = new System.IO.MemoryStream(structure);
			TypeInputStream tmis = new TypeInputStream(bais, core);
			
			try
			{
				// Read the object
				// We need to read the object and resolve all the types to
				// the local identifiers.  After that we can write out the
				// structure in codes the host understands, then compare
				// the result with the internal version we already have.
				// We could have done a tree walker over the types, but
				// this is easier.
				
				System.Object definition = tmis.ReadObject(argot.meta.MetaDefinition_Fields.TYPENAME);
				return (TypeElement) definition;
			}
			catch (System.IO.IOException e)
			{
				throw new TypeException("failed reading structure:" + e.Message);
			}
		}
		
		public virtual int GetId(System.String name)
		{
			int i = _library.GetId(name);
						
			i = _map.FindKey(i);
			if (i == -1)
				throw new TypeException("not mapped");
			
			return i;
		}
		
		

		public virtual System.String GetName(int id)
		{
			return _library.GetName(GetSystemId(id));
		}

		
		public virtual TypeReader GetReader(int id)
		{

			return _library.GetReader(GetSystemId(id));
		}
		
		public virtual TypeElement GetStructure(int id)
		{
			return _library.GetStructure(GetSystemId(id));
		}
		
		public virtual TypeWriter GetWriter(int id)
		{
			return _library.GetWriter(GetSystemId(id));
		}
		
		public virtual System.Type GetClass(int id)
		{
			return _library.GetClass(GetSystemId(id));
		}
		
		public virtual int GetId(System.Type clss)
		{
			int id = _library.GetId(clss);

			id = _map.FindKey(id);
			if (id == -1)
				throw new TypeException("not mapped");
			
			return id;
		}
		
		public virtual int GetSystemId(int id)
		{
			int i = _map.FindValue(id);
			if (i == -1)
				throw new TypeException("not found");

			return i;
		}
		
		public virtual int GetId(int systemid)
		{						
			int id = _map.FindKey(systemid);
			if (id == -1)
				throw new TypeException("not found");
			
			return id;
		}
		
		public virtual bool IsValid(int id)
		{			
			int i = _map.FindValue(id);
			if (i == -1)
				return false;
			
			return true;
		}
		
		public virtual bool IsValid(System.String name)
		{
			int i;
			
			try
			{
				i = _library.GetId(name);
			}
			catch (TypeException)
			{
				return false;
			}
			
			i = _map.FindKey(i);
			if (i == -1)
				return false;
			
			return true;
		}
	}
}